// ------------------------一、类型--------------------------------
// 1.1 布尔类型
var flag:boolean=true
console.log('布尔类型', 'var flag:boolean=true', flag)

// 1.2 数字类型
var num:number=123
console.log('数字类型', 'var num:number=123', num)

// 1.3 字符串类型
var str:string="this is ts"
console.log('字符串leix', 'var str:string("this is ts")', str)

// 1.4 数组类型（array）ts中定义数组有两种方式

// 1、第一种方式
let arr:number[] = [1,2,3,4,5]
console.log('数组定义方式', 'let arrar:numer[]=[1,2,3,4,5]', arr)

let arr2:string[]=["php", "js", "go"]
console.log('数组定义方式', 'let arr2:string[]=["php", "js", "go"]', arr2)

// 第二种方式
let arr3:Array<number>=[1,2,3,4]
console.log('数组定义方式2', "let arr:Array<number>=[1,2,3,4]", arr3)

let arr4:Array<string>=["pgh", "js"]
console.log('数组定义方式3', 'let arr4:Array<string>=["php", "js"]', arr4)


// 1.5 元组类型（tuple）
let arr5:[string, number, boolean] = ["ts", 12, false]
console.log('元组', 'let arr5:[string, number, boolean]', arr5)

// 1.6 枚举类型
enum Flag {success=1, error=-1}
var f:Flag=Flag.success
var f2:Flag=Flag.error
console.log('枚举', 'enum Flag {success=1, error=-1}', f, f2)

enum Color {blue, yellow, green}
var color:Color = Color.yellow
console.log('enum Color {blue, yellow, green}', color)

// 1.7 任意类型
var anyt:any = 34
anyt="3443"
console.log(anyt)

// 1.8 null 和 undefine 其他类型（never类型）子类型
var ud:undefined
console.log(ud)


// 1.9 多重类型
var num2:number | undefined
console.log(num2)

var num3:null = null
console.log(num3)

var num4:number|null|undefined
console.log(num4)

// ---------------------------二、方法------------------------------
// 2.1 声明函数：方法无返回值，使用void类型
function run():void{
    console.log('run method done')
}
run()

// 2.2 匿名函数：方法有返回值，使用对应的类型
var fun2 = function():number{
    return 123
}

console.log(fun2())


// 2.3 ts 函数参数
function getInfo(name:string, age:number):string {
    return `${name}今年${age}岁了`
}

console.log('ts带参函数getInfo', getInfo('jojo', 24))


// 2.4 可选参数 ?
function getInfo2(name:string, age?:number):string {
    if (age) {
        return `${name}今年${age}岁了`
    } else {
        return `${name}的年龄保密`
    }
}
console.log(getInfo2("jojoli"))

// 2.5 默认参数
function getInfo3(name:string, age:number=18):string {
    return `${name}今年${age}岁了`
}
console.log(getInfo3('kk'))

// 2.6 剩余参数 变长参数，只能在最后一位，有且只有一个

function sum(sum:number, ...result:number[]):number {
    for (var i = 0; i < result.length; ++i) {
        sum+=result[i];
    }
    return sum;
}

console.log('剩余参数', sum(100, 1, 2, 3, 4, 5))


// 2.7 ts函数重载 

function multiImpl(name:string):string;

function multiImpl(name:string, age:number):string;

function multiImpl(str:any, age?:any):any {
    if(age) {
      return `我叫${str}年龄是${age}`
    } else {
        return `我叫${str}`
    } 
}

console.log(multiImpl('jojo'))
console.log(multiImpl('jojo', 123))


// 2.8 箭头函数

setTimeout(() => {
    console.log('箭头函数')
}, 1000)


// ---------------------------三、类----------------------------------

// 3.1 类的定义

class Person{
    name:string; // 属性 前面省略了public关键词

    constructor(name:string) { // 构造函数 实例化的时候触发的方法
        this.name = name;
    }

    run():string{
        return 'Person类实例run方法'
    }

    getName():string{
        return this.name;
    }

    setName(name:string):void {
        this.name = name;
    }
}

var p = new Person('jojo');
console.log(p.run())
p.setName('jojoli')
console.log(p.getName())


// 3.2 ts中实现类的继承 extends、super
// 父类和子类中有同样的方法，子类调用子类中的方法（重写）

class SubPerson extends Person{
    age:number;
    constructor(name:string, age:number) {
        super(name)
        this.age = age;
    }

    run():string{
        return `${this.name}今年${this.age},现在在运动`
    }
}

var p2 = new SubPerson('dj', 23);
console.log(p2.run())

// 3.3 类属性的修饰符  
/**
 * public 公有。在类里面、子类、类外面都可以访问
 * protected：保护类型，在类里面、子类里面可以访问，在类外面没法访问
 * privated： 私有 在类里面可以访问，子类和类外面不能访问
 * 
 * super不能访问父类非public属性
 * 属性不写修饰符，默认是public
 */

// 略 自行验证


// 3.4 静态属性 静态方法 static 关键字

class StaticT{
    static message:string = "static message"

    static sayHello(str:string):string {
        return 'static method:'  + str
    }
}

console.log(StaticT.sayHello('dj'))
console.log(StaticT.message)


// 3.5 多态： 父类定义各一方法，让子类去实现，每个子类有不同的表现

abstract class Animal {
    name:string;
    constructor(name:string) {
        this.name = name;
    }

    abstract eat():void;
}

class Dog extends Animal {
    constructor(name:string) {
        super(name)
    }

    eat() {
        console.log(`${this.name}吃粑粑`)
    }
}

class Cat extends Animal {
    constructor(name:string) {
        super(name)
    }

    eat() {
        console.log(`${this.name}吃猫粮`)
    }
}

var cat:Animal = new Cat('速八的猫')
cat.eat()

// 3.6 abstract 抽象类和抽象方法
/**
    abstract 修饰符
    抽象类不可实例化，抽象方法子类必须实现
    抽象方法必须放在抽象类中
 */

// ------------------------四、接口-----------------------------

// 增加了灵活的接口类型： 属性、方法


// 4.1 属性类接口      

// 4.1.1 对json的约束 类似string、number

interface FullName {
    fistName:string; // 注意;结尾
    lastName:string;
    title?:string;  // ? 可选属性
}

function printName(name:FullName) {
    if(name.title) {
        console.log(`${name.title}: ${name.fistName}·${name.lastName}`)
    } else {
        console.log(`${name.fistName}·${name.lastName}`)
    }
}

var obj = {fistName: '阿莱克斯', lastName:'塔萨', title: '生命的缚誓者', str:'hihi'}
printName(obj) // 包含接口属性就行
printName({fistName: '阿莱克斯', lastName:'塔萨', title: '生命的缚誓者'}) // 只能包含接口属性

// 4.1.2 接口：可选属性

printName({fistName: '跳蛙', lastName:'骑士'}) // 只能包含接口属性


// 4.2 函数类型接口 对方法传入的参数和返回值进行约束

// 加密的函数类型接口
interface encrypt {
    (key:string, value:string):string;
}

var md5:encrypt=function(key:string, value:string):string {
    // 模拟操作
    return key + value + key
}

var sha1:encrypt=function(key:string, value:string):string {
    // 模拟操作
    return value + key + value
}

console.log('函数类型接口', md5('jojo', 'li'), sha1('鹦鹉', '瑞文'))



// 4.3 可索引接口  数组对象的约束 不常用

// 4.3.1 对数组的约束 索引是数字，类型为string
interface UserArr {
    [index:number]:string
}

var userArr = ['123', '1233']
console.log('可索引接口-数组', userArr[0])

// 4.3.2 对对象的约束

interface UserObj {
    [index:string]:string
}

var userObj:UserObj = {name: '20', age: '23'}  // 只能是字符串

// 4.4 类类型接口

interface IAble {
    move(str:string):void;
}

class Dragon extends Animal implements IAble {

    constructor(name:string) {
        super(name)
    }

    eat():void {
        console.log(`${this.name}吃buff`)
    }

    move(desc:string):void {
        console.log(`${this.name}用翅膀飞到${desc}`)
    }
}

var flyD:IAble = new Dragon('走地龙')
flyD.move('圣山')

var dragon:Dragon = new Dragon('青铜龙')
dragon.eat()

var d:Animal = new Dragon('精灵龙')
d.eat()



// 4.5 接口扩展  接口继承接口

interface SuperAnimal extends Animal {
    change():void;
} 


// -------------------------------------五、泛型-----------------------------------

// any 放弃类型检查， 泛型可以规范传入类型和返回类型

// 5.1 泛型方法

function getData<T>(value:T):T {
    return value
}

console.log('getData泛型', getData('2323'))
console.log('getData泛型', getData(233))

// 5.2 泛型类

class RandomListClass<T> {
    public list:T[]=[];

    add(num:T) {
        this.list.push(num)
    }

    get():T{
        // 略
        return this.list[Math.floor(Math.random() * this.list.length)]
    }
}

var m:RandomListClass<number> = new RandomListClass();
m.add(4555555555)
m.add(222222223)
m.add(345)
m.add(-3444444444444)
console.log('泛型类，随机值', m.get())

var m2:RandomListClass<string> = new RandomListClass();
m2.add('jojoli')
m2.add('dj')
m2.add('ly')
m2.add('jg')
console.log('泛型类，随机值', m2.get())

// 5.3 泛型接口

interface ConfigFn<T> {
   (value:T):T;  // 传入T，返回T
}

var fnT = function getData<T>(value:T):T {
    return value;
}

var instanceF:ConfigFn<string> = fnT

console.log(instanceF('42'))

// 5.4 把类作为参数类型的泛型接口

// 略 参考java class<T>做参数


// ----------------------------六、定义一个操作数据库的库---------------------------------

/**
    功能：定义一个数据库的库：支持mysql MSsql MongoDb
 */

interface DBI<T> {
    add(info:T):boolean;
    update(info:T, id:number):boolean;
    delete(id:number):boolean;
    get(id:number):any[];
}

class MysqlDb<T> implements DBI<T> {

    add(info: T): boolean {
        console.log(info)
        return true;  
        throw new Error("Method not implemented.")
    }
    update(info: T, id: number): boolean {
        throw new Error("Method not implemented.")
    }
    delete(id: number): boolean {
        throw new Error("Method not implemented.")
    }
    get(id: number): any[] {
        throw new Error("Method not implemented.")
    }
}

class MsSqlDb<T> implements DBI<T> {

    add(info: T): boolean {
        console.log(info);
    
        return true;
    }
    update(info: T, id: number): boolean {
        throw new Error("Method not implemented.")
    }
    delete(id: number): boolean {
        throw new Error("Method not implemented.")
    }
    get(id: number): any[] {
        throw new Error("Method not implemented.")
    }

}

class User{
    username:string | undefined;
    password:string | undefined;
}

var u = new User()
u.username='jojo';
u.password='34';

var oMssql = new MsSqlDb<User>();
oMssql.add(u)



// ----------------------------七、模块化-----------------------------
import {MsSqlDb as MsSqlDb2, MysqlDb as MysqlDb2} from './modules/db'

var db:MsSqlDb2<number> = new MsSqlDb2();
console.log(db.add(23333));

// 使用 node js/index.js 进行显示结果


// ----------------------------八、命名空间化-----------------------------------

// 避免命名冲突


import { A, B } from './modules/nsp'

var cat2:A.Animal = new A.Cat('速八的猫')
cat2.eat()

var cat3:B.Animal = new B.Cat('速八的猫')
cat3.eat()

// ------------------------------九、装饰器-------------------------------

// 9.1.2 类装饰器
function logClass(params:any) {
    console.log(params)
    params.prototype.apiUrl="fsfs"
}

@logClass
class HttpClient {
    constructor() {

    }

    getData() {

    }
}
var http:any = new HttpClient()
console.log(http.apiUrl)

// 9.1.1 装饰器工厂

function logClass2(params:string) {
    return function(target:any) {
        console.log(target);
        console.log(params);
        target.prototype.apiUrl=params;
    }
}

@logClass2("http://www.bilibili.com")
class HttpClient2 {
    constructor() {

    }
    getData() {

    }
}

var http2:any = new HttpClient2();
console.log(http2.apiUrl);

// 9.1.3 类装饰器重载构造函数

function logClass3(target:any) {
    console.log(target);
    return class extends target {
        apiUrl:any = '我是修改后的数据'

        getData() {
            console.log(this.apiUrl);
            
        }
    }
}

@logClass3
class HttpClient3{
    public apiUrl:string|undefined;
    constructor() {
        this.apiUrl = "我是构造函数中的url"
    }
    getData() {
        console.log(this.apiUrl);
        
    }
}

var http3:any = new HttpClient3()
http3.getData()



// 9.2 属性装饰器

// 类装饰器
function logClass4(params:string) {
    return function(target:any) {
        console.log(target);
        console.log(params);
    }
}

// 属性装饰器

function logProperty(params:any) {
    return function(target:any, attr:any) {
        console.log('logProperty', target);
        console.log('logProperty', attr);
    }
}


@logClass4('xxx')
class HttpClient4{
    @logProperty('baidu.com')
    public apiUrl:string|undefined;
    constructor() {
        this.apiUrl = "我是构造函数中的url"
    }
    getData() {
        console.log(this.apiUrl);
        
    }
}

var http4:any = new HttpClient4()
console.log(http4.apiUrl)

// 9.3 方法装饰器 
/**
 * 它会被应用到方法的属性描述上，可以用来监视，修改或者替换方法定义
 * 
 * 方法修饰会在运行时传入下列三个参数
 *  1、对于静态成员来说是类的构造函数，对于实例成员是类的原型对象
 *  2、成员的名称
 *  3、成员的属性描述符
 */

// 方法装饰器装饰器
function logMethod(params:any) {
    return function(target:any, methodName:any, desc:any) {
        console.log('logMethod', target);
        console.log('logMethod', methodName);
        console.log('logMethod', desc);

        target.apiUrl = 'xxx';
        target.run = function() {
            console.log('logMethod', 'run');
        }
    }
}

 class HttpClient5{
    public apiUrl:string|undefined;
    constructor() {
        this.apiUrl = "我是构造函数中的url"
    }
    @logMethod('hh')
    getData() {
        console.log(this.apiUrl);
        
    }
}

var http5:any = new HttpClient5();
http5.run()

// 方法装饰器修改参数类型

function get(params:any) {
    return function(target:any, methodName:any, desc:any) {
        console.log('get', target);
        console.log('get', methodName);
        console.log('get', desc);

        // 把装饰器方法里传入的所有参数改为string类型
        var oMethod=desc.value;
        desc.value=function(...args:any[]) {
            args = args.map((value)=> {
                return String(value)
            })
            console.log(args);
        }
    }
}

 class HttpClient6{
    public apiUrl:string|undefined;
    constructor() {
        this.apiUrl = "我是构造函数中的url"
    }
    @get('hhxhhx')
    getData(...args:any[]) {
        console.log(this.apiUrl);
    }
}

var http6:any = new HttpClient6();
http6.getData('jojo', 233, false)




// 9.4 方法参数装饰器 

function logParams(params:any) {
    return function(target:any, paramsName:any, paramIndex:any) {
        console.log('logParams', target);
        console.log('logParams', paramsName);
        console.log('logParams', paramIndex);

        target.apiUrl=params;
    }
}

 class HttpClient7{
    public apiUrl:string|undefined;
    constructor() {
        this.apiUrl = "我是构造函数中的url"
    }
    getData(@logParams('uuid') uuid:any[]) {
        console.log(this.apiUrl);
    }
}

var http7:any = new HttpClient7()
http7.getData()


// 装饰器执行顺序

/**
    从里到外，从上到下，从右到左：
        属性装饰器
        方法参数装饰器2
        方法参数装饰器1
        方法装饰器
        类装饰器2
        类装饰器1
 */
namespace K {
    function logProperty(params:any) {
        return function(target:any, attr:any) {
            console.log('属性装饰器');
        }
    }
    function logMethod(params:any) {
        return function(target:any, methodName:any) {
            console.log('方法装饰器');
        }
    }

    function logClass(params:any) {
        console.log('类装饰器1');
    }

    function logClass2(params:any) {
        console.log('类装饰器2')
    }
   function logParams(params:any) {
        return function(target:any, paramsName:any, paramIndex:any) {
            console.log('方法参数装饰器1');
        }
    }
    function logParams2(params:any) {
        return function(target:any, paramsName:any, paramIndex:any) {
            console.log('方法参数装饰器2');
        }
    }

    @logClass
    @logClass2
    export class HttpClient{
        @logProperty()
        public apiUrl:string|undefined;
        constructor() {
        }
        @logMethod()
        getData(@logParams() uuid:any, @logParams2() value:any) {
            console.log('getData被调用了');
        }
    }
}

// 从里到外，从上到下，从右到左

var http8:K.HttpClient = new K.HttpClient()







